Challenge 1¶

1. Basics of Image and Signal Processing (LE1)¶

1.1. Image Properties¶

Day 1¶

Find or acquire 1-3 images related to your selected country Mexic0. The images should be suitable for demonstrating adjustments to image properties brightness and hue in experiments.

The pictures are from when i was living in Mexico.

repository at https://github.com/BR4GR/gbsv-challenges

code writen with the help of the Deep Dive repo chatgpt and opencv.org

In [1]:
import cv2 as cv
import librosa
import matplotlib.pyplot as plt
import numpy as np
import sounddevice as sd
import time as time1

from ipywidgets import interact, widgets
from matplotlib import pyplot as plt
from matplotlib.widgets import Slider, Button, TextBox
from scipy.io.wavfile import write
In [2]:
jellyfish = cv.imread('jellyfish.jpg')
print(f"{jellyfish.shape=}\n{jellyfish.dtype=}\n{np.max(jellyfish)=}\n{np.min(jellyfish)=}")
jellyfish = cv.cvtColor(jellyfish, cv.COLOR_BGR2RGB)

plt.imshow(jellyfish)
plt.show()

cavediving = cv.imread('cavediving.jpg')
print(f"{cavediving.shape=}\n{cavediving.dtype=}\n{np.max(cavediving)=}\n{np.min(cavediving)=}")
cavediving = cv.cvtColor(cavediving, cv.COLOR_BGR2RGB)

plt.imshow(cavediving)
plt.show()

sunrise = cv.imread('sunrise.jpg')
print(f"{sunrise.shape=}\n{sunrise.dtype=}\n{np.max(sunrise)=}\n{np.min(sunrise)=}")
sunrise = cv.cvtColor(sunrise, cv.COLOR_BGR2RGB)
plt.imshow(sunrise)
plt.show()
jellyfish.shape=(3648, 5472, 3)
jellyfish.dtype=dtype('uint8')
np.max(jellyfish)=np.uint8(255)
np.min(jellyfish)=np.uint8(0)
No description has been provided for this image
cavediving.shape=(3648, 5472, 3)
cavediving.dtype=dtype('uint8')
np.max(cavediving)=np.uint8(255)
np.min(cavediving)=np.uint8(0)
No description has been provided for this image
sunrise.shape=(4000, 6000, 3)
sunrise.dtype=dtype('uint8')
np.max(sunrise)=np.uint8(255)
np.min(sunrise)=np.uint8(0)
No description has been provided for this image
In [3]:
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt

# Function to adjust brightness for an RGB image
def adjust_brightness(image, brightness=0):
    """
    Adjust the brightness of an RGB image.
    
    Parameters:
    - image: Input image in RGB format
    - brightness: Amount to adjust brightness (can be positive or negative)
    
    Returns:
    - Brightness-adjusted image
    """
    image = np.int16(image)
    image = image + brightness
    image = np.clip(image, 0, 255)  # Ensure values stay within [0, 255]
    return np.uint8(image)

# Function to adjust hue for an RGB image
def adjust_hue(image, hue_shift=0):
    """
    Adjust the hue of an RGB image.
    
    Parameters:
    - image: Input image in RGB format
    - hue_shift: Amount to shift the hue by (in degrees)
    
    Returns:
    - Hue-adjusted image
    """
    hsv_image = cv.cvtColor(image, cv.COLOR_RGB2HSV)
    hue = hsv_image[:, :, 0].astype(int)
    hue = (hue + hue_shift) % 180  # Ensure hue stays within the range [0, 179]
    hsv_image[:, :, 0] = hue.astype(np.uint8)
    return cv.cvtColor(hsv_image, cv.COLOR_HSV2RGB)

# Function to apply both brightness and hue adjustments
def apply_adjustments(image, brightness=0, hue_shift=0):
    bright_image = adjust_brightness(image, brightness)
    final_image = adjust_hue(bright_image, hue_shift)
    return final_image

# Function to display the original and adjusted images side by side
def display_images_side_by_side(image, brightness=0, hue_shift=0, ax=None, title="Image"):
    """
    Display the original and adjusted images side by side for comparison.
    
    Parameters:
    - image: The input image in RGB format
    - brightness: Amount to adjust brightness
    - hue_shift: Amount to shift the hue by
    """
    adjusted_image = apply_adjustments(image, brightness, hue_shift)
    
    # Original image
    ax[0].imshow(image)
    ax[0].set_title(f'Original {title}')
    ax[0].axis('off')
    
    # Adjusted image
    ax[1].imshow(adjusted_image)
    ax[1].set_title(f'Adjusted {title} (Brightness: {brightness}, Hue: {hue_shift})')
    ax[1].axis('off')

# Function to display all images side by side with fixed brightness and hue values
def display_all_images():
    # Fixed values for brightness and hue for each image
    brightness_jellyfish = -40
    hue_jellyfish = 40
    
    brightness_cave = -10
    hue_cave = 20
    
    brightness_sunrise = 10
    hue_sunrise = -20

    fig, axes = plt.subplots(3, 2, figsize=(12, 12))

    # jellyfish
    display_images_side_by_side(jellyfish, brightness_jellyfish, hue_jellyfish, axes[0], "jellyfish")
    
    # Cave Diving
    display_images_side_by_side(cavediving, brightness_cave, hue_cave, axes[1], "cave diving")
    
    # sunrise
    display_images_side_by_side(sunrise, brightness_sunrise, hue_sunrise, axes[2], "sunrise")
    
    plt.tight_layout()
    plt.savefig("adjusted_images2x3.png", dpi=300)
    plt.show()

# Display all images with fixed adjustments
display_all_images()
No description has been provided for this image

Day 2¶

Define a problem and use case related to your country profile (Steckbrief) for each assigned property that you intend to address (brightness and hue). Then define 1-2 experiments with objectives on how you want to address your defined problem and use case. Multiple experiments/objectives can be analyzed in one single image. Write concisely for each experiment: WHAT problem and use case do you address, HOW do you want to address/solve the problem for this specific use case, and WHY is your experiment helpful for the chosen use case?

Problem:¶

The jellyfish image was taken in a aquarium in playa del Carmen, the room was extreemly dark even thought it was full of different changing colorfull lights, which ilumnated the jelyfish. the image is too bright, and the variety of colors from the light show is not well represented. The dark room and colorful glowing effect of the jellyfish are lost due to overexposure. This results in the background becoming too visible and reduces the visual focus on the jellyfish, which were meant to glow against a dark backdrop.

Use Case:¶

Simulating the original light show where jellyfish glowed in different colors in a dark room. The objective is to recreate the low-light ambiance and simulate the various colors the jellyfish displayed during the light show using hue adjustments.

Objectives of the Experiments:¶

Brightness Adjustment: Reduce the overall brightness to restore the dark environment, ensuring that the jellyfish glow remains the visual focus. How: Lower the brightness to darken the background while keeping the jellyfish visible, mimicking the darkroom atmosphere. we will test and compare different methodes of brightness ajustment, like gamma, region speciffic brightness, or clahe.

Hue Adjustment:* Simulate the changing colors of the light show by adjusting the hue. This allows us to create the effect of different colored lights shining on the jellyfish, even though the image only shows one color. How: Shift the hue to enhance the jellyfish’s glow and replicate the dynamic color changes seen during the light show.

Explanation, Why are these adjustments relevant?¶

Brightness: The dark room is an essential part of the jellyfish exhibit, where overexposure distorts the intended visual experience. Reducing brightness restores this intended ambiance.

Hue: The light show was dynamic and colorful, and adjusting the hue allows us to simulate the color transitions that were key to the experience. This makes the image more visually accurate and engaging.

Day 3¶

Conduct part 1 of your experiments with your images and suitable methods. Reason your choice of parameters and methods using visualizations or 1-2 sentences emach.

In [4]:
# Function to apply gamma correction
def adjust_gamma(image, gamma=1.0):
    inv_gamma = 1.0 / gamma
    table = np.array([(i / 255.0) ** inv_gamma * 255 for i in np.arange(0, 256)]).astype("uint8")
    return cv.LUT(image, table)

# Function to apply region-specific brightness adjustment (e.g., darkening background)
def apply_region_specific_brightness(image, brightness_value):
    mask = image[:, :, 2] < 100  # Apply brightness adjustment to the darker regions only
    brightened_image = adjust_brightness(image.copy(), brightness_value)
    image[mask] = brightened_image[mask]
    return image

def apply_clahe(image, clip_limit=2.0, tile_grid_size=(8, 8)):
    lab_image = cv.cvtColor(image, cv.COLOR_RGB2LAB)
    clahe = cv.createCLAHE(clipLimit=clip_limit, tileGridSize=tile_grid_size)
    lab_image[:, :, 0] = clahe.apply(lab_image[:, :, 0])
    return cv.cvtColor(lab_image, cv.COLOR_LAB2RGB)


def apply_vignette(image, radius_factor=2, brightness_reduction=0.5):
    rows, cols = image.shape[:2]
    center_x, center_y = cols // 2, rows // 2
    # Create a Gaussian kernel that will serve as the vignette mask
    # Calculate the maximum possible radius
    max_radius = np.sqrt(center_x ** 2 + center_y ** 2) / radius_factor
    # Create meshgrid for the X and Y coordinates
    x = np.arange(cols)
    y = np.arange(rows)
    X, Y = np.meshgrid(x, y)
    # Calculate the distance from the center of the image
    distance_from_center = np.sqrt((X - center_x) ** 2 + (Y - center_y) ** 2)
    # Normalize the distances to a range between 0 and 1, then reverse (1 at center, 0 at edges)
    vignette_mask = np.clip(1 - (distance_from_center / max_radius), 0, 1)
    # Apply brightness reduction (e.g., 0.5 means reduce brightness by 50% at the edges)
    vignette_mask = vignette_mask ** brightness_reduction  # Adjust the strength of reduction
    # Apply the vignette mask to each channel (RGB)
    vignette_mask_3d = vignette_mask[:, :, np.newaxis]  # Make mask compatible with 3-channel image
    vignette_image = np.uint8(image * vignette_mask_3d)
    return vignette_image


# Experiment: Create three levels of brightness adjustment (80%, 60%, 40%)
brightness_levels = [-15, -30, -45]
gamma_values = [0.85, 0.7, 0.55]
vignette_values = [0.85, 0.7, 0.55]
clahe_clip_limits = [1.0, 2.0, 3.0]

# Create a 3x3 grid for each method and level of adjustment
fig, ax = plt.subplots(5, 3, figsize=(15, 13))

# Row 1: Linear brightness adjustment
for i, brightness in enumerate(brightness_levels):
    adjusted_image = adjust_brightness(jellyfish.copy(), brightness)
    ax[0, i].imshow(adjusted_image)
    ax[0, i].set_title(f'Linear Brightness: {100 + brightness}%')
    ax[0, i].axis('off')

# Row 3: Region-specific brightness adjustment
for i, brightness in enumerate(brightness_levels):
    region_adjusted_image = apply_region_specific_brightness(jellyfish.copy(), brightness)
    ax[1, i].imshow(region_adjusted_image)
    ax[1, i].set_title(f'Region-Specific: {100 + brightness}%')
    ax[1, i].axis('off')
    
# Row 2: Gamma correction
for i, gamma in enumerate(gamma_values):
    gamma_corrected_image = adjust_gamma(jellyfish.copy(), gamma)
    ax[2, i].imshow(gamma_corrected_image)
    ax[2, i].set_title(f'Gamma Correction: {gamma}')
    ax[2, i].axis('off')

# Row 4: CLAHE with different clip limits (strengths)
for i, clip_limit in enumerate(clahe_clip_limits):
    clahe_image = apply_clahe(jellyfish.copy(), clip_limit=clip_limit)
    ax[3, i].imshow(clahe_image)
    ax[3, i].set_title(f'CLAHE: Clip Limit = {clip_limit}')
    ax[3, i].axis('off')

# Row 1: Linear brightness adjustment
for i, brightness in enumerate(vignette_values):
    adjusted_image = apply_vignette(jellyfish.copy(), 1, brightness)
    ax[4, i].imshow(adjusted_image)
    ax[4, i].set_title(f'vignette Correction: {brightness}')
    ax[4, i].axis('off')


plt.tight_layout()
plt.savefig("brightnessexperiment.png", dpi=300)
plt.show()
No description has been provided for this image

Reasoning for Methods and Parameters:¶

Gamma Correction was chosen for its ability to apply non-linear adjustments, which better handles darkening shadows while preserving highlights, making it the best method for enhancing the contrast between the glowing jellyfish and the dark background. CLAHE was chosen for localized contrast enhancement, particularly in low-contrast areas (background). Region-specific adjustments were used to target specific areas like the background, maintaining the focus on the jellyfish. The vignette effect was used as a creative approach to enhance the vintage feel and control the outer brightness. Parameters were chosen by trial and error, using large and small stept untill a godd step size was found for each Method.

Observations:¶

The best result was achieved with Gamma Correction (0.55), which effectively darkened the background while preserving the vibrant, glowing jellyfish. The vignette effect added a vintage feel but wasn't the best fit for the goal of simulating the aquarium light show.

In [5]:
dark_adjusted_jellyfish = adjust_gamma(jellyfish.copy(), 0.55)

# Display the original and adjusted images side by side
fig, ax = plt.subplots(1, 2, figsize=(12, 6))
ax[0].imshow(jellyfish)
ax[0].set_title('Original Jellyfish')
ax[0].axis('off')

ax[1].imshow(dark_adjusted_jellyfish)
ax[1].set_title('Adjusted Jellyfish: Gamma = 0.55')
ax[1].axis('off')

plt.tight_layout()
plt.savefig("dark_adjusted_jellyfish.png", dpi=300)
plt.show()
No description has been provided for this image

Day 4¶

Conduct part 2 of your experiments with your images and suitable methods. R

In [6]:
hue_shifts = [-80, -60, -40, -20, 0, 20, 40, 60, 80]

# Create a 3x3 grid for hue adjustments
fig, ax = plt.subplots(3, 3, figsize=(12, 12))

for i, hue_shift in enumerate(hue_shifts):
    hue_adjusted_image = adjust_hue(dark_adjusted_jellyfish.copy(), hue_shift=hue_shift)
    
    row = i // 3
    col = i % 3
    
    ax[row, col].imshow(hue_adjusted_image)
    ax[row, col].set_title(f'Hue Shift: {hue_shift}°')
    ax[row, col].axis('off')

plt.tight_layout()
plt.savefig("hue_experiment.png", dpi=300)
plt.show()
No description has been provided for this image
In [7]:
# Apply hue shifts to different quadrants of the image
def apply_quadrant_hue_shifts(image, hue_shifts):
    # Define the quadrants
    #jellyfish.shape=(3648, 5472, 3)
    rowsplit = 2000
    columnsplit = 3150

    top_left = image[0:rowsplit, 0:columnsplit]
    top_right = image[0:rowsplit, columnsplit:]
    bottom_right = image[rowsplit:, columnsplit:]
    bottom_left = image[rowsplit:, 0:columnsplit]
    
    # Apply hue shifts to each quadrant
    top_left = adjust_hue(top_left, hue_shifts[0])
    top_right = adjust_hue(top_right, hue_shifts[1])
    bottom_right = adjust_hue(bottom_right, hue_shifts[2])
    bottom_left = adjust_hue(bottom_left, hue_shifts[3])
    
    # Reconstruct the full image with the adjusted quadrants
    top_half = np.hstack((top_left, top_right))
    bottom_half = np.hstack((bottom_left, bottom_right))
    adjusted_image = np.vstack((top_half, bottom_half))
    filename = f"quadrant_hue_shifts{hue_shifts[0]}_{hue_shifts[1]}_{hue_shifts[2]}_{hue_shifts[3]}.png"
    cv.imwrite(filename, cv.cvtColor(adjusted_image, cv.COLOR_RGB2BGR))
    print(f"Image saved as {filename}")

apply_quadrant_hue_shifts(dark_adjusted_jellyfish.copy(), [0, 40, 80, 120])
apply_quadrant_hue_shifts(dark_adjusted_jellyfish.copy(), [20, 60, 100, 140])
Image saved as quadrant_hue_shifts0_40_80_120.png
Image saved as quadrant_hue_shifts20_60_100_140.png

awesome Image¶

awesome Image

awesome Image¶

awesome Image